home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / mac / imfapp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-02  |  42.3 KB  |  1,722 lines  |  [TEXT/KAHL]

  1. /* Program that translates and previews image families for Mac Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* This is a simple app whose function is to translate and preview
  10.    Xconq images and image families. */
  11.  
  12. #include "config.h"
  13. #include "misc.h"
  14. #include "lisp.h"
  15. #include "imf.h"
  16.  
  17. #ifdef THINK_C
  18. #include <MacHeaders>
  19. #else /* probably MPW */
  20. #include <Types.h>
  21. #include <Memory.h>
  22. #include <Resources.h>
  23. #include <QuickDraw.h>
  24. #include <Values.h>
  25. #include <Fonts.h>
  26. #include <Events.h>
  27. #include <Windows.h>
  28. #include <Menus.h>
  29. #include <Dialogs.h>
  30. #include <Desk.h>
  31. #include <ToolUtils.h>
  32. #include <SegLoad.h>
  33. #include <Files.h>
  34. #include <Folders.h>
  35. #include <OSUtils.h>
  36. #include <OSEvents.h>
  37. #include <DiskInit.h>
  38. #include <Packages.h>
  39. #include <Traps.h>
  40. #include <StandardFile.h>
  41. #endif
  42.  
  43. #include "macimf.h"
  44.  
  45. #ifdef MPW
  46. #define QD(whatever) (qd.##whatever)
  47. #define QDPat(whatever) (&(qd.##whatever))
  48. #endif
  49. #ifdef THINK_C
  50. #define QD(whatever) (whatever)
  51. #define QDPat(whatever) (whatever)
  52. #endif
  53.  
  54. /* Definitions for the menus. */
  55.  
  56. #define mbMain 128
  57.  
  58. #define mApple 128
  59.  
  60. #define miAbout 1
  61.  
  62. #define mFile 129
  63.  
  64. #define miFileNew 1
  65. #define miFileOpen 2
  66. #define miFileAddImf 3
  67. #define miFileAddResources 4
  68. /* 5 */
  69. #define miFileSave 6
  70. #define miFileSaveImf 7
  71. #define miFileSaveResources 8
  72. #define miFileSelectedOnly 9
  73. /* 10 */
  74. #define miFileQuit 11
  75.  
  76. #define mEdit 130
  77.  
  78. #define miEditCut 1
  79. #define miEditCopy 2
  80. #define miEditPaste 3
  81. #define miEditClear 4
  82.  
  83. #define mView 131
  84. #define miView4x4 1
  85. #define miView8x8 2
  86. #define miView16x16 3
  87. #define miView32x32 4
  88. #define miView64x64 5
  89. /* 6 */
  90. #define miViewColor 7
  91. #define miViewNames 8
  92. #define miViewMask 9
  93. /* 10 */
  94. #define miViewAsUnit 11
  95. #define miViewAsTerrain 12
  96. #define miViewAsEmblem 13
  97. #define miViewWithUnit 14
  98. #define miViewWithTerrain 15
  99. #define miViewWithEmblem 16
  100. /* 17 */
  101. #define miViewIcons 18
  102. #define miViewTiles 19
  103.  
  104. #define wImages 128
  105.  
  106. #define aAbout 128
  107. #define aWarning 140
  108. #define aError 141
  109.  
  110. #define is_selected(imf) (imf == selected_imf)
  111.  
  112. /* Global variables. */
  113.  
  114. int debug_output = 1;
  115.  
  116. /* Variables tweaked by menu items. */
  117.  
  118. int write_all = 1;
  119.  
  120. int show_color = 1;
  121. int show_names = 1;
  122. int show_mask = 1;
  123.  
  124. int vary_unit = 1;
  125. int vary_terrain = 0;
  126. int vary_emblem = 0;
  127.  
  128. int with_unit = 0;
  129. int with_terrain = 0;
  130. int with_emblem = 0;
  131.  
  132. int select_icons = 1;
  133. int select_tiles = 1;
  134.  
  135. /* Pointers to the image families being held constant for display. */
  136.  
  137. ImageFamily *const_terrain = NULL;
  138.  
  139. ImageFamily *const_unit = NULL;
  140.  
  141. ImageFamily *const_emblem = NULL;
  142.  
  143. int iw = 16, ih = 16;
  144.  
  145. int hasColorQD;
  146.  
  147. int useWNE;
  148.  
  149. char spbuf[1000];
  150. char tmpbuf[1000];
  151.  
  152. Str255 tmpstr;
  153.  
  154. /* Resource ids. */
  155.  
  156. short nextimageid;
  157. short nextpatid;
  158. short nextppatid;
  159. short nextsicnid;
  160. short nexticonid;
  161. short nextcicnid;
  162. short nextcolorid;
  163. short nextimfid;
  164.  
  165. /* Pointer to storage for all the image families. */
  166.  
  167. ImageFamily *selected_imf = NULL;
  168.  
  169. int selected_n = 0;
  170.  
  171. WindowPtr imagewin = NULL;
  172.  
  173. Rect dragrect = { -32000, -32000, 32000, 32000 };
  174. Rect sizerect;
  175.  
  176. /* This variable leaves space for two lines of text at the top of the window. */
  177.  
  178. int toplineh = 32;
  179.  
  180. /* The one scrollbar. */
  181.  
  182. ControlHandle vscrollbar = nil;
  183.  
  184. Rect vscrollrect;
  185.  
  186. int sbarwid = 15;
  187.  
  188. int numvisrows = 0;
  189.  
  190. int firstvisrow = 0;
  191.  
  192. int winwidth, winheight;
  193.  
  194. /* The numbers of rows and columns of images. */
  195.  
  196. int rows, cols;
  197.  
  198. /* The size of the rectangle allocated to a single image. */
  199.  
  200. int eltw, elth;
  201.  
  202. CursHandle watchcursor;
  203.  
  204. /* Function prototypes. */
  205.  
  206. void do_event(EventRecord *event);
  207. void grow_scrollbar(void);
  208. void do_menu_command(long which);
  209. void adjust_menus(void);
  210. void clear_everything(void);
  211. pascal void scroll_proc(ControlHandle control, short code);
  212. void handle_mouse_click(Point mouse, int mods);
  213. void force_update(void);
  214.  
  215. void imf_callback(ImageFamily *imf, int loadnow);
  216.  
  217. void open_imf_dir_file(void);
  218. void open_imf_file(void);
  219. void open_resource_file(void);
  220. void collect_all_resources(ResType typ);
  221. void collect_all_colors(ResType typ);
  222.  
  223. void save_imf_dir_file(void);
  224. void save_imf_file(void);
  225. void save_resource_file(void);
  226. void make_imf_resources(ImageFamily *imf);
  227. void make_imc_resources(ImageColor *imc);
  228.  
  229. void update_image_window(void);
  230. void draw_topline(void);
  231. void draw_one_image(ImageFamily *imf, int col, int row);
  232. void set_scrollbar(void);
  233. void invert_selected_imf(void);
  234. void draw_as_terrain_image(int sx, int sy, int sw, int sh, ImageFamily *imf);
  235. void draw_as_unit_image(WindowPtr win, int sx, int sy, int sw, int sh, ImageFamily *imf);
  236. void draw_as_emblem_image(WindowPtr win, int ex, int ey, int ew, int eh, ImageFamily *imf);
  237.  
  238. char *copy_pascal_string(char *str);
  239.  
  240. /* The program proper. */
  241.  
  242. int
  243. main()
  244. {
  245.     SysEnvRec se;
  246.     Handle menubar;
  247.     MenuHandle menu;
  248.     RgnHandle cursorrgn;
  249.     Boolean gotevent;
  250.     EventRecord    event;
  251.  
  252.     InitGraf(&QD(thePort));
  253.     InitFonts();
  254.     FlushEvents(everyEvent, 0);
  255.     InitWindows();
  256.     InitMenus();
  257.     TEInit();
  258.     InitDialogs(NULL);
  259.     InitCursor();
  260.     watchcursor = GetCursor(watchCursor);
  261.  
  262.     SysEnvirons(2, &se);
  263.     hasColorQD = se.hasColorQD;
  264.     /* Set up the menu bar.  No trickery needed. */
  265.     menubar = GetNewMBar(mbMain);
  266.     SetMenuBar(menubar);
  267.     /* Add the DAs etc as usual. */
  268.     if ((menu = GetMHandle(mApple)) != nil) {
  269.         AddResMenu(menu, 'DRVR');
  270.     }
  271.     DrawMenuBar();
  272.  
  273.     init_lisp();
  274.  
  275.     /* Create the main window we're going to play in. */
  276.     if (hasColorQD) {
  277.         imagewin = GetNewCWindow(wImages, NULL, (WindowPtr) -1L);
  278.     } else {
  279.         imagewin = GetNewWindow(wImages, NULL, (WindowPtr) -1L);
  280.     }
  281.     vscrollrect = imagewin->portRect;
  282.     vscrollrect.left = vscrollrect.right - sbarwid;  vscrollrect.top += toplineh - 1;
  283.     vscrollrect.right += 1;  vscrollrect.bottom -= sbarwid - 1;
  284.     vscrollbar =
  285.         NewControl(imagewin, &vscrollrect, "\p", 1, 0, 0, 100, scrollBarProc, 0L);
  286.     ShowWindow(imagewin);
  287.  
  288.     sizerect.top = 50;
  289.     sizerect.left = 50;
  290.     sizerect.bottom = QD(screenBits).bounds.bottom - QD(screenBits).bounds.top;
  291.     sizerect.right  = QD(screenBits).bounds.right  - QD(screenBits).bounds.left;
  292.  
  293.     useWNE = (NGetTrapAddress(0x60, ToolTrap) != NGetTrapAddress(0x9f, ToolTrap));
  294.     /* Pass WNE an empty region the 1st time thru. */
  295.     cursorrgn = NewRgn();
  296.     while (1) {
  297.         /* Use WaitNextEvent if it is available. */
  298.         if (useWNE) {
  299.             gotevent = WaitNextEvent(everyEvent, &event, 0L, cursorrgn);
  300.         } else {
  301.             SystemTask();
  302.             gotevent = GetNextEvent(everyEvent, &event);
  303.         }
  304.         if (gotevent) {
  305.             do_event(&event);
  306.         }
  307.     }
  308.     return 0;
  309. }
  310.  
  311. /* Given an event, figure out what to do with it. */
  312.  
  313. void
  314. do_event(event)
  315. EventRecord *event;
  316. {
  317.     short part, err;
  318.     WindowPtr window;
  319.     char key;
  320.     Point pnt;
  321.     long winsize;
  322.     GrafPtr oldport;
  323.  
  324.     switch (event->what) {
  325.         case mouseDown:
  326.             part = FindWindow(event->where, &window);
  327.             switch (part) {
  328.                 case inMenuBar:
  329.                     adjust_menus();
  330.                     do_menu_command(MenuSelect(event->where));
  331.                     break;
  332.                 case inSysWindow:
  333.                     SystemClick(event, window);
  334.                     break;
  335.                 case inContent:
  336.                     if (window != FrontWindow()) {
  337.                         SelectWindow(window);
  338.                         /* We just want to discard the event, since clicks in a
  339.                            windows are sometimes irreversible actions. */
  340.                         adjust_menus();
  341.                     } else {
  342.                         handle_mouse_click(event->where, event->modifiers);
  343.                     }
  344.                     break;
  345.                 case inDrag:
  346.                     DragWindow(window, event->where, &dragrect);
  347.                     break;
  348.                 case inGrow:
  349.                     winsize = GrowWindow(window, event->where, &sizerect);
  350.                     if (winsize != 0) {
  351.                         GetPort(&oldport);
  352.                         SetPort(window);
  353.                         EraseRect(&window->portRect);
  354.                         SizeWindow(window, LoWord(winsize), HiWord(winsize), 1);
  355.                         grow_scrollbar();
  356.                         InvalRect(&window->portRect);
  357.                         SetPort(oldport);
  358.                     }
  359.                     break;
  360.                 case inZoomIn:
  361.                 case inZoomOut:
  362.                     if (TrackBox(window, event->where, part)) {
  363.                         GetPort(&oldport);
  364.                         /* The window must be the current port. (ZoomWindow bug) */
  365.                         SetPort(window);
  366.                         EraseRect(&window->portRect);
  367.                         ZoomWindow(window, part, true);
  368.                         grow_scrollbar();
  369.                         InvalRect(&window->portRect);
  370.                         SetPort(oldport);
  371.                     }
  372.                     break;
  373.                 case inGoAway:
  374.                     /* Don't mess around, just shut down. */
  375.                     ExitToShell();
  376.                     break;
  377.             }
  378.             break;
  379.         case mouseUp:
  380.             part = FindWindow(event->where, &window);
  381.             switch (part) {
  382.                 case inContent:
  383.                     if (0 /* up in diff window than down? */) {
  384.                     } else {
  385.                     }
  386.                     break;
  387.             }
  388.             break;
  389.         case keyDown:
  390.         case autoKey:
  391.             key = event->message & charCodeMask;
  392.             /* Check for menukey equivalents. */
  393.             if (event->modifiers & cmdKey) {
  394.                 if (event->what == keyDown) {
  395.                     adjust_menus();
  396.                     do_menu_command(MenuKey(key));
  397.                 }
  398.             } else {
  399.                 if (event->what == keyDown) {
  400.                     /* Random keypress, interpret it. */
  401.                 }
  402.             }
  403.             break;
  404.         case activateEvt:
  405.             break;
  406.         case updateEvt:
  407.             if (imagewin == ((WindowPtr) event->message)) {
  408.                 update_image_window();
  409.             }
  410.             break;
  411.         case diskEvt:
  412.             /*    Call DIBadMount in response to a diskEvt, so that the user can format
  413.                  a floppy. (from DTS Sample) */
  414.             if (HiWord(event->message) != noErr) {
  415.                 SetPt(&pnt, 50, 50);
  416.                 err = DIBadMount(pnt, event->message);
  417.             }
  418.             break;
  419. #if 0
  420.         case kOSEvent:
  421.         /*    1.02 - must BitAND with 0x0FF to get only low byte */
  422.             switch ((event->message >> 24) & 0x0FF) {        /* high byte of message */
  423.                 case kSuspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  424.                     gInBackground = (event->message & kResumeMask) == 0;
  425.                     DoActivate(FrontWindow(), !gInBackground);
  426.                     break;
  427.             }
  428.             break;
  429. #endif
  430.         default:
  431.             break;
  432.     }
  433. }
  434.  
  435. void
  436. grow_scrollbar()
  437. {
  438.     Rect tmprect = imagewin->portRect;
  439.  
  440.     MoveControl(vscrollbar, tmprect.right - sbarwid, toplineh - 1);
  441.     SizeControl(vscrollbar, sbarwid + 1, tmprect.bottom - tmprect.top - toplineh - sbarwid + 1 + 1);
  442. }
  443.  
  444. /* Decipher and do a menu command. */
  445.  
  446. void
  447. do_menu_command(which)
  448. long which;
  449. {
  450.     short menuid, menuitem;
  451.     Str255 daname;
  452.     short darefnum;
  453.  
  454.     menuid = HiWord(which);
  455.     menuitem = LoWord(which);
  456.     switch (menuid) {
  457.         case mApple:
  458.             switch (menuitem) {
  459.                 case miAbout:
  460.                     Alert(aAbout, nil);
  461.                     break;
  462.                 default:
  463.                     GetItem(GetMHandle(mApple), menuitem, daname);
  464.                     darefnum = OpenDeskAcc(daname);
  465.             }
  466.             break;
  467.         case mFile:
  468.             switch (menuitem) {
  469.                 case miFileNew:
  470.                     clear_everything();
  471.                     /* Clean up the display (should not be necessary though). */
  472.                     force_update();
  473.                     break;
  474.                 case miFileOpen:
  475.                     open_imf_dir_file();
  476.                     /* Clean up the display (should not be necessary though). */
  477.                     force_update();
  478.                     break;
  479.                 case miFileAddImf:
  480.                     open_imf_file();
  481.                     /* Clean up the display (should not be necessary though). */
  482.                     force_update();
  483.                     break;
  484.                 case miFileAddResources:
  485.                     open_resource_file();
  486.                     /* Clean up the display (should not be necessary though). */
  487.                     force_update();
  488.                     break;
  489.                 case miFileSave:
  490.                     save_imf_dir_file();
  491.                     break;
  492.                 case miFileSaveImf:
  493.                     save_imf_file();
  494.                     break;
  495.                 case miFileSaveResources:
  496.                     save_resource_file();
  497.                     break;
  498.                 case miFileSelectedOnly:
  499.                     write_all = !write_all;
  500.                     break;
  501.                 case miFileQuit:
  502.                     ExitToShell();
  503.                     break;
  504.             }
  505.             break;
  506.         case mEdit:
  507.             /* handledbyda = SystemEdit(menuitem-1); */
  508.             switch (menuitem)  {
  509.                 case miEditCut:
  510.                     break;
  511.                 case miEditCopy:
  512.                     break;
  513.                 case miEditPaste:
  514.                     break;
  515.                 case miEditClear:
  516.                     break;
  517.             }
  518.             break;
  519.         case mView:
  520.             switch (menuitem) {
  521.                 case miView4x4:
  522.                     iw = ih = 4;
  523.                     break;
  524.                 case miView8x8:
  525.                     iw = ih = 8;
  526.                     break;
  527.                 case miView16x16:
  528.                     iw = ih = 16;
  529.                     break;
  530.                 case miView32x32:
  531.                     iw = ih = 32;
  532.                     break;
  533.                 case miView64x64:
  534.                     iw = ih = 64;
  535.                     break;
  536.                 case miViewColor:
  537.                     show_color = !show_color;
  538.                     break;
  539.                 case miViewNames:
  540.                     show_names = !show_names;
  541.                     break;
  542.                 case miViewMask:
  543.                     show_mask = !show_mask;
  544.                     break;
  545.                 case miViewAsUnit:
  546.                     vary_unit = 1;
  547.                     vary_terrain = 0;
  548.                     vary_emblem = 0;
  549.                     break;
  550.                 case miViewAsTerrain:
  551.                     vary_unit = 0;
  552.                     vary_terrain = 1;
  553.                     vary_emblem = 0;
  554.                     break;
  555.                 case miViewAsEmblem:
  556.                     vary_unit = 0;
  557.                     vary_terrain = 0;
  558.                     vary_emblem = 1;
  559.                     break;
  560.                 case miViewWithUnit:
  561.                     with_unit = !with_unit;
  562.                     const_unit = selected_imf;
  563.                     break;
  564.                 case miViewWithTerrain:
  565.                     with_terrain = !with_terrain;
  566.                     const_terrain = selected_imf;
  567.                     break;
  568.                 case miViewWithEmblem:
  569.                     with_emblem = !with_emblem;
  570.                     const_emblem = selected_imf;
  571.                     break;
  572.                 case miViewIcons:
  573.                     select_icons = !select_icons;
  574.                     break;
  575.                 case miViewTiles:
  576.                     select_tiles = !select_tiles;
  577.                     break;
  578.             }
  579.             force_update();
  580.             break;
  581.     }
  582.     HiliteMenu(0);
  583. }
  584.  
  585. /* Set enabling and decoration of menus to reflect current state. */
  586.  
  587. void
  588. adjust_menus()
  589. {
  590.     MenuHandle menu;
  591.  
  592.     if ((menu = GetMHandle(mFile)) != nil) {
  593.         CheckItem(menu, miFileSelectedOnly, !write_all);
  594.     }
  595.     if ((menu = GetMHandle(mView)) != nil) {
  596.         CheckItem(menu, miView4x4, (iw == 4));
  597.         CheckItem(menu, miView8x8, (iw == 8));
  598.         CheckItem(menu, miView16x16, (iw == 16));
  599.         CheckItem(menu, miView32x32, (iw == 32));
  600.         CheckItem(menu, miView64x64, (iw == 64));
  601.         if (hasColorQD) {
  602.             EnableItem(menu, miViewColor);
  603.             CheckItem(menu, miViewColor, show_color);
  604.         } else {
  605.             DisableItem(menu, miViewColor);
  606.         }
  607.         CheckItem(menu, miViewNames, show_names);
  608.         CheckItem(menu, miViewMask, show_mask);
  609.         CheckItem(menu, miViewAsUnit, vary_unit);
  610.         CheckItem(menu, miViewAsTerrain, vary_terrain);
  611.         CheckItem(menu, miViewAsEmblem, vary_emblem);
  612.         CheckItem(menu, miViewWithUnit, with_unit);
  613.         if ((selected_imf != NULL && !vary_unit) || with_unit) {
  614.             EnableItem(menu, miViewWithUnit);
  615.         } else {
  616.             DisableItem(menu, miViewWithUnit);
  617.         }
  618.         CheckItem(menu, miViewWithTerrain, with_terrain);
  619.         if ((selected_imf != NULL && !vary_terrain) || with_terrain) {
  620.             EnableItem(menu, miViewWithTerrain);
  621.         } else {
  622.             DisableItem(menu, miViewWithTerrain);
  623.         }
  624.         CheckItem(menu, miViewWithEmblem, with_emblem);
  625.         if ((selected_imf != NULL && !vary_emblem) || with_emblem) {
  626.             EnableItem(menu, miViewWithEmblem);
  627.         } else {
  628.             DisableItem(menu, miViewWithEmblem);
  629.         }
  630.         CheckItem(menu, miViewIcons, select_icons);
  631.         CheckItem(menu, miViewTiles, select_tiles);
  632.     }
  633. }
  634.  
  635. /* Empty all data, files, etc. */
  636.  
  637. void
  638. clear_everything()
  639. {
  640.     numimages = 0;
  641.     numcolors = 0;
  642.  
  643.     selected_imf = NULL;
  644.     selected_n = 0;
  645.     const_terrain = NULL;
  646.     const_unit = NULL;
  647.     const_emblem = NULL;
  648.     numvisrows = firstvisrow = 0;
  649. }
  650.  
  651. pascal void
  652. scroll_proc(control, code)
  653. ControlHandle control;
  654. short code;
  655. {
  656.     int curvalue, jump;
  657.  
  658.     curvalue = GetCtlValue(control);
  659.     switch (code) {
  660.         case inPageDown:
  661.             jump = (numvisrows > 2 ? numvisrows - 2 : 1);
  662.             break;
  663.         case inDownButton:
  664.             jump = 1;
  665.             break;
  666.         case inPageUp:
  667.             jump = - (numvisrows > 2 ? numvisrows - 2 : 1);
  668.             break;
  669.         case inUpButton:
  670.             jump = -1;
  671.             break;
  672.         default:
  673.             jump = 0;
  674.             break;
  675.     }
  676.     curvalue += jump;
  677.     SetCtlValue(control, curvalue);
  678. }
  679.  
  680. void
  681. handle_mouse_click(mouse, mods)
  682. Point mouse;
  683. int mods;
  684. {
  685.     int row, col, n;
  686.     ControlHandle control;
  687.     short part, oldvalue;
  688.  
  689.     SetPort(imagewin);
  690.     GlobalToLocal(&mouse);
  691.     part = FindControl(mouse, imagewin, &control);
  692.     if (control == vscrollbar) {
  693.         oldvalue = GetCtlValue(vscrollbar);
  694.         switch (part) {
  695.             case inThumb:
  696.                 part = TrackControl(control, mouse, NULL);
  697.                 break;
  698.             default:
  699.                 part = TrackControl(control, mouse, (ProcPtr) scroll_proc);
  700.                 break;
  701.         }
  702.         firstvisrow = GetCtlValue(vscrollbar);
  703.         if (oldvalue != firstvisrow) {
  704.             force_update();
  705.         }
  706.     } else if (mouse.v <= toplineh) {
  707.         /* Clicking in the topline area de-selects always. */
  708.         invert_selected_imf();
  709.         selected_imf = NULL;
  710.         draw_topline();
  711.     } else {
  712.         /* Figure out which image was clicked on. */
  713.         col = mouse.h / eltw;
  714.         row = (mouse.v - toplineh) / elth + firstvisrow;
  715.         n = row * cols + col;
  716.         if (n >= 0 && n < numimages) {
  717.             if (!is_selected(images[n])) {
  718.                 invert_selected_imf();
  719.                 selected_imf = images[n];
  720.                 selected_n = n;
  721.                 invert_selected_imf();
  722.             }
  723.             if (mods & cmdKey) {
  724.                 with_terrain = 1;
  725.                 const_terrain = selected_imf;
  726.                 force_update();
  727.             }
  728.             if (mods & optionKey) {
  729.                 with_emblem = 1;
  730.                 const_emblem = selected_imf;
  731.                 force_update();
  732.             }
  733.         } else {
  734.             invert_selected_imf();
  735.             selected_imf = NULL;
  736.         }
  737.         draw_topline();
  738.     }
  739. }
  740.  
  741. void
  742. force_update()
  743. {
  744.     GrafPtr oldport;
  745.  
  746.     GetPort(&oldport);
  747.     SetPort(imagewin);
  748.     EraseRect(&imagewin->portRect);
  749.     InvalRect(&imagewin->portRect);
  750.     SetPort(oldport);
  751. }
  752.  
  753. /* INPUT/OUTPUT */
  754.  
  755. void
  756. imf_callback(imf, loadnow)
  757. ImageFamily *imf;
  758. int loadnow;
  759. {
  760.     if (loadnow && imf != NULL)
  761.       mac_interp_imf(imf);
  762.     /* Do some visual feedback periodically. */
  763.     if (numimages % 10 == 0)
  764.       draw_topline();
  765. }
  766.  
  767. void
  768. open_imf_dir_file()
  769. {
  770.     char filename[BUFSIZE];
  771.     int startlineno = 0, endlineno = 0;
  772.     FILE *fp;
  773.     Point pnt;
  774.     SFTypeList typelist;
  775.     SFReply reply;
  776.  
  777.     /* Gotta be somewhere... */
  778.     SetPt(&pnt, 100, 100);
  779.     /* Only read text files. */
  780.     typelist[0] = 'TEXT';
  781.     SFGetFile(pnt, "\p", NULL, 1, typelist, NULL, &reply);
  782.     if (reply.good) {
  783.         /* Make the location of the file be the current volume. */
  784.         SetVol(reply.fName, reply.vRefNum);
  785.         p2c(((char *) reply.fName), filename);
  786.         SetCursor(*watchcursor);
  787.         fp = fopen(filename, "r");
  788.         if (fp != NULL) {
  789.             load_image_families(fp, TRUE, imf_callback);
  790.             fclose(fp);
  791.         }
  792.         /* Sort all into alphabetical order. */
  793.         sort_all_images();
  794.         sort_all_colors();
  795.         SetCursor(&QD(arrow));
  796.     }
  797. }
  798.  
  799. /* Open and read/interpret the contents of an imf file. */
  800.  
  801. void
  802. open_imf_file()
  803. {
  804.     char filename[BUFSIZE];
  805.     Point pnt;
  806.     SFTypeList typelist;
  807.     SFReply reply;
  808.  
  809.     /* Gotta be somewhere... */
  810.     SetPt(&pnt, 100, 100);
  811.     /* Only read text files. */
  812.     typelist[0] = 'TEXT';
  813.     SFGetFile(pnt, "\p", NULL, 1, typelist, NULL, &reply);
  814.     if (reply.good) {
  815.         /* Make the location of the file be the current volume. */
  816.         SetVol(reply.fName, reply.vRefNum);
  817.         p2c(((char *) reply.fName), filename);
  818.         SetCursor(*watchcursor);
  819.         load_imf_file(filename, imf_callback);
  820.         /* Sort all into alphabetical order. */
  821.         sort_all_images();
  822.         sort_all_colors();
  823.         SetCursor(&QD(arrow));
  824.     }
  825. }
  826.  
  827. /* Get the name of a resource file, open it, and make image families out
  828.    of all appropriate resource types. */
  829.  
  830. void
  831. open_resource_file()
  832. {
  833.     Point pnt;
  834.     SFTypeList typelist;
  835.     SFReply reply;
  836.  
  837.     /* Gotta be somewhere... */
  838.     SetPt(&pnt, 100, 100);
  839.     /* Pick up any sort of file. */
  840.     SFGetFile(pnt, "\p", NULL, -1, typelist, NULL, &reply);
  841.     if (reply.good) {
  842.         SetVol(reply.fName, reply.vRefNum);
  843.         SetCursor(*watchcursor);
  844.         if (OpenResFile(reply.fName) != -1) {
  845.             /* Now that all the resources are available, go through all types of rsrcs
  846.                that might have useful images. */
  847.             collect_all_resources('XCif');
  848.             collect_all_resources('cicn');
  849.             collect_all_resources('ICON');
  850.             collect_all_resources('SICN');
  851.             collect_all_resources('ppat');
  852.             collect_all_resources('PAT ');
  853.             collect_all_colors('XCic');
  854.             /* Sort all into alphabetical order. */
  855.             sort_all_images();
  856.             sort_all_colors();
  857.         } else {
  858.             init_warning("could not open resource file");
  859.         }
  860.         SetCursor(&QD(arrow));
  861.     }
  862. }
  863.  
  864. /* Given a resource type, get image families for all resources of that type. */
  865.  
  866. void
  867. collect_all_resources(ResType typ)
  868. {
  869.     int i, n;
  870.     char *imfname;
  871.     ImageFamily *imf;
  872.     Handle handle;
  873.     short resid;  ResType restype;  Str255 resname;
  874.  
  875.     n = CountResources(typ);
  876.     for (i = 0; i < n; ++i ) {
  877.         imfname = NULL;
  878.         handle = GetIndResource(typ, i + 1);
  879.         GetResInfo(handle, &resid, &restype, resname);
  880.         if (resname[0] > 0) {
  881.             imfname = copy_pascal_string((char *) resname);
  882.         } else {
  883.             /* (should warn about unnamed resources being ignored?) */
  884.         }
  885.         if (imfname != NULL
  886.             /* Resources with names ending in " mask" are masks for other resources,
  887.                so ignore them here. */
  888.             && strcmp(imfname+strlen(imfname)-5, " mask") != 0) {
  889.             imf = get_imf(imfname);
  890.             mac_load_imf(imf);
  891.             /* Do some visual feedback periodically. */
  892.             if (numimages % 10 == 0)
  893.               draw_topline();
  894.         }
  895.     }
  896. }
  897.  
  898. /* Given a resource type, get named colors for all resources of that type. */
  899.  
  900. void
  901. collect_all_colors(ResType typ)
  902. {
  903.     int i, n;
  904.     char *imcname;
  905.     ImageColor *imc;
  906.     Handle handle;
  907.     short resid;  ResType restype;  Str255 resname;
  908.  
  909.     n = CountResources(typ);
  910.     for (i = 0; i < n; ++i ) {
  911.         imcname = NULL;
  912.         handle = GetIndResource(typ, i + 1);
  913.         GetResInfo(handle, &resid, &restype, resname);
  914.         if (resname[0] > 0) {
  915.             imcname = copy_pascal_string((char *) resname);
  916.         } else {
  917.             /* (should warn about unnamed resources being ignored?) */
  918.         }
  919.         if (imcname != NULL) {
  920.             imc = get_imc(imcname);
  921.             mac_load_image_color(imc);
  922.             /* Do some visual feedback periodically. */
  923.             if (numcolors % 10 == 0)
  924.               draw_topline();
  925.         }
  926.     }
  927. }
  928.  
  929. void
  930. save_imf_dir_file()
  931. {
  932.     int i;
  933.     char filename[BUFSIZE], *loc;
  934.     Point pnt;
  935.     FILE *fp;
  936.     SFReply reply;
  937.     ImageFamily *imf;
  938.  
  939.     SetPt(&pnt, 100, 100);
  940.     SFPutFile(pnt, "\p", "\pimf.dir", nil, &reply);
  941.     if (reply.good) {
  942.         /* Make the location of the file be the current volume. */
  943.         SetVol(reply.fName, reply.vRefNum);
  944.         p2c(((char *) reply.fName), filename);
  945.         SetCursor(*watchcursor);
  946.         /* (should genericize into a write_imf_dir that takes array of images) */
  947.         fp = fopen(filename, "w");
  948.         if (fp != NULL) {
  949.             fprintf(fp, "ImageFamilyName FileName\n");
  950.             for (i = 0; i < numimages; ++i) {
  951.                 imf = images[i];
  952.                 if (write_all || is_selected(imf)) {
  953.                     loc = "???";
  954.                     if (imf->location && !empty_string(imf->location->name))
  955.                       loc = imf->location->name;
  956.                     fprintf(fp, "%s %s\n", imf->name, loc);
  957.                 }
  958.                 /* (to write imf files, should scan through images once for
  959.                     each file, writing all images found that are in that file) */
  960.             }
  961.             fprintf(fp, ". .\n");
  962.             fclose(fp);
  963.         } else {
  964.             run_warning("could not open file for writing");
  965.         }
  966.         SetCursor(&QD(arrow));
  967.     }
  968. }
  969.  
  970. /* Write all the images into an imf file. */
  971.  
  972. void
  973. save_imf_file()
  974. {
  975.     int i;
  976.     char filename[BUFSIZE];
  977.     Point pnt;
  978.     FILE *fp;
  979.     SFReply reply;
  980.  
  981.     SetPt(&pnt, 100, 100);
  982.     SFPutFile(pnt, "\p", "\pimages.imf", nil, &reply);
  983.     if (reply.good) {
  984.         /* Make the location of the file be the current volume. */
  985.         SetVol(reply.fName, reply.vRefNum);
  986.         p2c(((char *) reply.fName), filename);
  987.         SetCursor(*watchcursor);
  988.         fp = fopen(filename, "w");
  989.         if (fp != NULL) {
  990.             /* Write out the imf forms of all the image families. */
  991.             for (i = 0; i < numimages; ++i) {
  992.                 if (write_all || is_selected(images[i])) {
  993.                     make_generic_image_data(images[i]);
  994.                     write_imf(fp, images[i]);
  995.                 }
  996.             }
  997.             /* Write out the image colors. */
  998.             for (i = 0; i < numcolors; ++i) {
  999.                 if (write_all) {  /* (should have a way to select colors) */
  1000.                     write_imc(fp, colors[i]);
  1001.                 }
  1002.             }
  1003.             fclose(fp);
  1004.         } else {
  1005.             run_warning("could not open file for writing");
  1006.         }
  1007.         SetCursor(&QD(arrow));
  1008.     }
  1009. }
  1010.  
  1011. /* Write all the images, as resources, into a resource file. */
  1012.  
  1013. void
  1014. save_resource_file()
  1015. {
  1016.     int refnum, i;
  1017.     Point pnt;
  1018.     SFReply reply;
  1019.  
  1020.     SetPt(&pnt, 100, 100);
  1021.     SFPutFile(pnt, "\p", "\pimages.imf Images", nil, &reply);
  1022.     if (reply.good) {
  1023.         /* Make the location of the file be the current volume. */
  1024.         SetVol(reply.fName, reply.vRefNum);
  1025.         SetCursor(*watchcursor);
  1026.         CreateResFile(reply.fName);
  1027.         refnum = OpenResFile(reply.fName);
  1028.         if (refnum >= 0) {
  1029.             /* All synthesized resource ids should go from 1000 up. */
  1030.             nextpatid = 1000;
  1031.             nextppatid = 1000;
  1032.             nextsicnid = 1000;
  1033.             nexticonid = 1000;
  1034.             nextcicnid = 1000;
  1035.             nextcolorid = 1000;
  1036.             nextimfid = 1000;
  1037.             /* Make resources for named colors. */
  1038.             for (i = 0; i < numcolors; ++i) {
  1039.                 make_imc_resources(colors[i]);
  1040.             }
  1041.             /* Make resources for image family specifications. */
  1042.             for (i = 0; i < numimages; ++i) {
  1043.                 if (write_all || is_selected(images[i])) {
  1044.                     make_imf_resources(images[i]);
  1045.                 }
  1046.             }
  1047.             /* Closing the resource file causes actual writing. */
  1048.             CloseResFile(refnum);
  1049.         } else {
  1050.             run_warning("could not open resource file");
  1051.         }
  1052.         SetCursor(&QD(arrow));
  1053.     }
  1054. }
  1055.  
  1056. void
  1057. make_imf_resources(imf)
  1058. ImageFamily *imf;
  1059. {
  1060.     int j, makexcif = FALSE, needimghead;
  1061.     char buf[BUFSIZE], xcifbuf[1000];
  1062.     Handle imfhandle, handle;
  1063.     Str255 tmpstr, tmpstrmask;
  1064.     Image *img;
  1065.     MacImage *macimg;
  1066.  
  1067.     if (imf == NULL || imf->name == NULL)
  1068.       return;
  1069.     /* Make the resource names that we will use. */
  1070.     c2p(imf->name, tmpstr);
  1071.     sprintf(buf, "%s mask", imf->name);
  1072.     c2p(buf, tmpstrmask);
  1073.     sprintf(xcifbuf, "(");
  1074.     if (imf->location && imf->location->name) {
  1075.         sprintf(xcifbuf+strlen(xcifbuf), "(in \"%s\")", imf->location->name);
  1076.         makexcif = TRUE;
  1077.     }
  1078.     for (img = imf->images; img != NULL; img = img->next) {
  1079.         needimghead = 1;
  1080.         if (img->istile || img->embedname) {
  1081.             sprintf(xcifbuf+strlen(xcifbuf), " ((%d %d", img->w, img->h);
  1082.             if (img->istile)
  1083.               strcat(xcifbuf, " tile");
  1084.             strcat(xcifbuf, ")");
  1085.             needimghead = 0;
  1086.             if (img->embedname) {
  1087.                 sprintf(xcifbuf+strlen(xcifbuf), " (embed \"%s\")", img->embedname);
  1088.             }
  1089.             makexcif = TRUE;
  1090.         }
  1091.         if (!needimghead)
  1092.           strcat(xcifbuf, ")");
  1093.         macimg = get_mac_image(img);
  1094.         /* Now make Mac-specific resources for image cases that we know about. */
  1095.         if (macimg->patdefined) {
  1096.             handle = NewHandle(8);
  1097.             for (j = 0; j < 8; ++j) {
  1098.                 (*handle)[j] = macimg->monopat[j];
  1099.             }
  1100.             AddResource(handle, 'PAT ', nextpatid++, tmpstr);
  1101.         }
  1102.         if (macimg->monosicn != nil) {
  1103.             if (macimg->masksicn != nil) {
  1104.                 handle = NewHandle(64);
  1105.                 /* Add the mask as a second sicn. */
  1106.                 for (j = 0; j < 32; ++j)
  1107.                   (*handle)[j+32] = (*(macimg->masksicn))[j];
  1108.             } else {
  1109.                 handle = NewHandle(32);
  1110.             }
  1111.             for (j = 0; j < 32; ++j)
  1112.               (*handle)[j] = (*(macimg->monosicn))[j];
  1113.             AddResource(handle, 'SICN', nextsicnid++, tmpstr);
  1114.         } else if (macimg->masksicn != nil) {
  1115.             /* A weird case, but don't lose it. */
  1116.             handle = NewHandle(32);
  1117.             for (j = 0; j < 32; ++j)
  1118.               (*handle)[j] = (*(macimg->masksicn))[j];
  1119.             AddResource(handle, 'SICN', nextsicnid++, tmpstrmask);
  1120.         }
  1121.         if (macimg->monoicon != nil) {
  1122.             handle = NewHandle(128);
  1123.             for (j = 0; j < 128; ++j)
  1124.               (*handle)[j] = (*(macimg->monoicon))[j];
  1125.             AddResource(handle, 'ICON', nexticonid++, tmpstr);
  1126.         }
  1127.         if (macimg->maskicon != nil) {
  1128.             handle = NewHandle(128);
  1129.             for (j = 0; j < 128; ++j)
  1130.               (*handle)[j] = (*(macimg->maskicon))[j];
  1131.             AddResource(handle, 'ICON', nexticonid++, tmpstrmask);
  1132.         }
  1133.         if (hasColorQD && macimg->colrpat != nil) {
  1134.             int patsize, h, pixmapsize, ctabsize, i;
  1135.             char *addr;
  1136.             Rect bounds;
  1137.             PixPatHandle pixpathandle;
  1138.             PixPat tmppixpat;
  1139.             PixMapHandle pmhandle;
  1140.             PixMap tmppixmap;
  1141.             CTabHandle ctabhandle;
  1142.  
  1143.             pixpathandle = macimg->colrpat;
  1144.             pmhandle = (*pixpathandle)->patMap;
  1145.             bounds = (*pmhandle)->bounds;
  1146.             h = bounds.bottom - bounds.top;
  1147.             pixmapsize = h * ((*pmhandle)->rowBytes & 0x3fff);
  1148.             ctabhandle = (*pmhandle)->pmTable;
  1149.             ctabsize = 8 + 8 * ((*ctabhandle)->ctSize + 1);
  1150.             /* Make a copy of the pixpat and bash its pointers/handles. */
  1151.              memcpy(&tmppixpat, *(pixpathandle), sizeof(PixPat));
  1152.              tmppixpat.patMap = (PixMapHandle) (sizeof(PixPat));
  1153.              tmppixpat.patData = (Handle) (sizeof(PixPat) + sizeof(PixMap));
  1154.             tmppixpat.patXData = 0;
  1155.             tmppixpat.patXValid = -1;
  1156.             tmppixpat.patXMap = 0;
  1157.             if (macimg->patdefined) {
  1158.                 for (i = 0; i < 8; ++i)
  1159.                   ((char *) &tmppixpat.pat1Data)[i] = ((char *) &(macimg->monopat))[i];
  1160.             } else {
  1161.                 for (i = 0; i < 8; ++i)
  1162.                   ((char *) &tmppixpat.pat1Data)[i] = 0;
  1163.             }
  1164.             /* Make a copy of the pixmap and bash it. */
  1165.             memcpy(&tmppixmap, *pmhandle, sizeof(PixMap));
  1166.             tmppixmap.rowBytes |= 0x8000;
  1167.             tmppixmap.pmTable = (CTabHandle) (sizeof(PixPat) + sizeof(PixMap) + pixmapsize);
  1168.             /* Now allocate a handle for the resource and fill it in. */
  1169.             patsize = sizeof(PixPat) + sizeof(PixMap) + pixmapsize + ctabsize;
  1170.             handle = NewHandle(patsize);
  1171.             /* Fill up the handle. */
  1172.             HLock(handle);
  1173.             addr = *handle;
  1174.             memset(addr, 0, patsize);
  1175.             memcpy(addr, &tmppixpat, sizeof(PixPat));
  1176.             addr += sizeof(PixPat);
  1177.             memcpy(addr, &tmppixmap, sizeof(PixMap));
  1178.             addr += sizeof(PixMap);
  1179.             memcpy(addr, *((*pixpathandle)->patData), pixmapsize);
  1180.             addr += pixmapsize;
  1181.             memcpy(addr, *ctabhandle, ctabsize);
  1182.             HUnlock(handle);
  1183.             AddResource(handle, 'ppat', nextppatid++, tmpstr);
  1184.         }
  1185.         if (hasColorQD && macimg->colricon != nil) {
  1186.             int cicnsize, h, pixmapsize, masksize, monosize, ctabsize, zero = 0;
  1187.             char *addr;
  1188.             Rect bounds;
  1189.             CIconHandle cicnhandle;
  1190.             PixMap tmppixmap;
  1191.             BitMap tmpmaskbitmap;
  1192.             BitMap tmpmonobitmap;
  1193.             CTabHandle ctabhandle;
  1194.  
  1195.             cicnhandle = (CIconHandle) macimg->colricon;
  1196.             bounds = (*cicnhandle)->iconPMap.bounds;
  1197.             h = bounds.bottom - bounds.top;
  1198.             pixmapsize = h * ((*cicnhandle)->iconPMap.rowBytes & 0x3fff);
  1199.             ctabhandle = (*cicnhandle)->iconPMap.pmTable;
  1200.             ctabsize = 8 + 8 * ((*ctabhandle)->ctSize + 1);
  1201.             bounds = (*cicnhandle)->iconMask.bounds;
  1202.             h = bounds.bottom - bounds.top;
  1203.             masksize = h * (*cicnhandle)->iconMask.rowBytes;
  1204.             monosize = h * (*cicnhandle)->iconMask.rowBytes;
  1205.             /* Make a copy of the pixmap and bash it. */
  1206.             memcpy(&tmppixmap, &((*cicnhandle)->iconPMap), sizeof(PixMap));
  1207.             tmppixmap.rowBytes |= 0x8000;
  1208.             tmppixmap.pmTable = (CTabHandle) (sizeof(PixMap) + 2 * sizeof(BitMap) + 4
  1209.                        + masksize + monosize);
  1210.             /* Make a copy of the mask bitmap and bash it. */
  1211.             memcpy(&tmpmaskbitmap, &((*cicnhandle)->iconMask), sizeof(BitMap));
  1212.             /* Make a copy of the mono bitmap and bash it. */
  1213.             memcpy(&tmpmonobitmap, &((*cicnhandle)->iconBMap), sizeof(BitMap));
  1214.             /* Now allocate a handle for the resource and fill it in. */
  1215.             cicnsize = sizeof(PixMap) + 2 * sizeof(BitMap) + 4
  1216.                        + masksize + monosize + ctabsize + pixmapsize;
  1217.             handle = NewHandle(cicnsize);
  1218.             /* Fill up the handle. */
  1219.             HLock(handle);
  1220.             addr = *handle;
  1221.             memset(addr, 0, cicnsize);
  1222.             memcpy(addr, &tmppixmap, sizeof(PixMap));
  1223.             addr += sizeof(PixMap);
  1224.             memcpy(addr, &tmpmaskbitmap, sizeof(BitMap));
  1225.             addr += sizeof(BitMap);
  1226.             memcpy(addr, &tmpmonobitmap, sizeof(BitMap));
  1227.             addr += sizeof(BitMap);
  1228.             memcpy(addr, &zero, 4);
  1229.             addr += 4;
  1230.             memcpy(addr, (char *) (*cicnhandle)->iconMaskData, masksize);
  1231.             addr += masksize;
  1232.             memcpy(addr, ((char *) (*cicnhandle)->iconMaskData) + masksize, monosize);
  1233.             addr += monosize;
  1234.             memcpy(addr, *ctabhandle, ctabsize);
  1235.             addr += ctabsize;
  1236.             memcpy(addr, *((*cicnhandle)->iconData), pixmapsize);
  1237.             HUnlock(handle);
  1238.             AddResource(handle, 'cicn', nextcicnid++, tmpstr);
  1239.         }
  1240.     }
  1241.     /* Close out the XCif data and make it into a resource too. */
  1242.     strcat(xcifbuf, ")");
  1243.     if (makexcif) {
  1244.         imfhandle = NewHandle(strlen(xcifbuf)+1);
  1245.         strcpy(*imfhandle, xcifbuf);
  1246.         AddResource(imfhandle, 'XCif', nextimfid++, tmpstr);
  1247.     }
  1248. }
  1249.  
  1250. void
  1251. make_imc_resources(imc)
  1252. ImageColor *imc;
  1253. {
  1254.     Handle handle;
  1255.  
  1256.     handle = NewHandle(6);
  1257.     ((short *) (*handle))[0] = imc->r;
  1258.     ((short *) (*handle))[1] = imc->g;
  1259.     ((short *) (*handle))[2] = imc->b;
  1260.     c2p(imc->name, tmpstr);
  1261.     AddResource(handle, 'XCic', nextcolorid++, tmpstr);
  1262. }
  1263.  
  1264. /* GRAPHICS */
  1265.  
  1266. /* Totally redraw the one window. */
  1267.  
  1268. void
  1269. update_image_window()
  1270. {
  1271.     int row, col, n;
  1272.     GrafPtr oldport;
  1273.  
  1274.     BeginUpdate(imagewin);
  1275.     GetPort(&oldport);
  1276.     SetPort(imagewin);
  1277.     EraseRect(&(imagewin->portRect));
  1278.     draw_topline();
  1279.     eltw = (show_names ? 100 : iw + 4);  elth = ih + 4;
  1280.     /* (should be determined by choice of app font) */
  1281.     if (elth < 10) elth = 10;
  1282.     winwidth = imagewin->portRect.right - imagewin->portRect.left;
  1283.     winheight = imagewin->portRect.bottom - imagewin->portRect.top;
  1284.     if (with_terrain && const_terrain) {
  1285.         if (best_image(const_terrain, iw, ih)->istile) {
  1286.             draw_as_terrain_image(0, toplineh,
  1287.                                   winwidth - sbarwid, winheight - toplineh - sbarwid,
  1288.                                   const_terrain);
  1289.         } else {
  1290.             /* (should draw under each unit separately) */
  1291.         }
  1292.     }
  1293.     /* Compute how many columns we can fit, rounding down but always at least 1. */
  1294.     cols = winwidth / eltw;
  1295.     if (cols <= 0) cols = 1;
  1296.     /* We can get a little wider spacing by recalculating the element width. */
  1297.     eltw = (winwidth - 10) / cols;
  1298.     rows = numimages / cols + 1;
  1299.     numvisrows = (winheight - toplineh - 15) / elth;
  1300.     if (numvisrows > rows)
  1301.       numvisrows = rows;
  1302.     if (firstvisrow + numvisrows > rows)
  1303.       firstvisrow = rows - numvisrows;
  1304.     for (row = firstvisrow; row < (firstvisrow + numvisrows); ++row) {
  1305.         for (col = 0; col < cols; ++col) {
  1306.             n = row * cols + col;
  1307.             if (n >= numimages) break;
  1308.             draw_one_image(images[n], col, row);
  1309.         }
  1310.     }
  1311.     invert_selected_imf();
  1312.     set_scrollbar();
  1313.     DrawControls(imagewin);
  1314.     DrawGrowIcon(imagewin);
  1315.     SetPort(oldport);
  1316.     EndUpdate(imagewin);
  1317. }
  1318.  
  1319. void
  1320. draw_topline()
  1321. {
  1322.     int first;
  1323.     Image *img;
  1324.     Rect tmprect;
  1325.     GrafPtr oldport;
  1326.  
  1327.     GetPort(&oldport);
  1328.     SetPort(imagewin);
  1329.     tmprect = imagewin->portRect;
  1330.     tmprect.bottom = tmprect.top + toplineh;
  1331.     EraseRect(&tmprect);
  1332.     TextFont(monaco);
  1333.     TextSize(9);
  1334.     sprintf(spbuf, "%d images", numimages);
  1335.     if (with_terrain && const_terrain != NULL) {
  1336.         sprintf(spbuf+strlen(spbuf), " (on %s terrain)", const_terrain->name);
  1337.     }
  1338.     if (with_emblem && const_emblem != NULL) {
  1339.         sprintf(spbuf+strlen(spbuf), " (with %s emblem)", const_emblem->name);
  1340.     }
  1341.     if (numcolors > 0) {
  1342.         sprintf(spbuf+strlen(spbuf), ", %d colors", numcolors);
  1343.     }
  1344.     c2p(spbuf, tmpstr);
  1345.     MoveTo(4, 12);
  1346.     DrawString(tmpstr);
  1347.     /* Draw a second line describing the selection. */
  1348.     if (selected_imf != NULL) {
  1349.         strcpy(spbuf, "[");
  1350.         strcat(spbuf, selected_imf->name);
  1351.         if (selected_imf->location && selected_imf->location->name)
  1352.           sprintf(spbuf+strlen(spbuf), " (in \"%s\")", selected_imf->location->name);
  1353.         switch (selected_imf->numsizes) {
  1354.             case 0:
  1355.                 strcat(spbuf, " (no images)");
  1356.                 break;
  1357.             case 1:
  1358.                 sprintf(spbuf+strlen(spbuf), " (1 size: %dx%d)",
  1359.                         selected_imf->images->w, selected_imf->images->h);
  1360.                 break;
  1361.             default:
  1362.                 sprintf(spbuf+strlen(spbuf), " (%d sizes:", selected_imf->numsizes);
  1363.                 first = TRUE;
  1364.                 for (img = selected_imf->images; img != NULL; img = img->next) {
  1365.                     if (first)
  1366.                       first = FALSE;
  1367.                     else
  1368.                       strcat(spbuf, ",");
  1369.                     sprintf(spbuf+strlen(spbuf), " %dx%d", img->w, img->h);
  1370.                 }
  1371.                 strcat(spbuf, ")");
  1372.                 break;
  1373.         }
  1374.         strcat(spbuf, "]");
  1375.         c2p(spbuf, tmpstr);
  1376.         MoveTo(4, 26);
  1377.         DrawString(tmpstr);
  1378.     }
  1379.     /* Draw a dividing line. */
  1380.     MoveTo(0, toplineh - 2);
  1381.     Line(imagewin->portRect.right, 0);
  1382.     /* Restore the existing port. */
  1383.     SetPort(oldport);
  1384. }
  1385.  
  1386. /* Draw a single imf at a given row and column. */
  1387.  
  1388. void
  1389. draw_one_image(imf, col, row)
  1390. ImageFamily *imf;
  1391. int col, row;
  1392. {
  1393.     int namex, namey;
  1394.     Rect tmprect, maskrect;
  1395.     FontInfo fontinfo;
  1396.  
  1397.     tmprect.left = col * eltw;  tmprect.top = toplineh + (row - firstvisrow) * elth;
  1398.     tmprect.right = tmprect.left + iw + 4;  tmprect.bottom = tmprect.top + ih + 4;
  1399.     InsetRect(&tmprect, 2, 2);
  1400.     /* Maybe draw the background terrain. (should be in a hex shape sometimes?) */
  1401.     if (vary_terrain) {
  1402.         draw_as_terrain_image(tmprect.left, tmprect.top, iw, ih, imf);
  1403.     } else if (with_terrain && const_terrain) {
  1404.         draw_as_terrain_image(tmprect.left-2, tmprect.top-2, iw+4, elth, const_terrain);
  1405.     }
  1406.     if (vary_unit) {
  1407.         draw_as_unit_image(imagewin, tmprect.left, tmprect.top, iw, ih, imf);
  1408.     } else if (with_unit && const_unit) {
  1409.         draw_as_unit_image(imagewin, tmprect.left, tmprect.top, iw, ih, const_unit);
  1410.     }
  1411.     /* Maybe draw an emblem. */
  1412.     if (vary_emblem) {
  1413.         draw_as_emblem_image(imagewin, tmprect.left + iw - 8, tmprect.top, 8, 8, imf);
  1414.     } else if (with_emblem && const_emblem) {
  1415.         draw_as_emblem_image(imagewin, tmprect.left + iw - 8, tmprect.top, 8, 8, const_emblem);
  1416.     }
  1417.     /* Maybe draw the name of the image. */
  1418.     if (show_names) {
  1419.         /* If nonwhite background, add a white rect for the name. */
  1420.         namex = col * eltw + iw + 4;
  1421.         namey = toplineh + (row - firstvisrow) * elth + (elth / 2) + 5;
  1422.         if (with_terrain && const_terrain) {
  1423.             GetFontInfo(&fontinfo);
  1424.             maskrect.left = namex;
  1425.             maskrect.top = namey - fontinfo.ascent;
  1426.             maskrect.right = maskrect.left + TextWidth(imf->name, 0, strlen(imf->name));
  1427.             maskrect.bottom = namey + fontinfo.descent + 1;
  1428.             FillRect(&maskrect, QD(white));
  1429.         }
  1430.         MoveTo(namex, namey);
  1431.         c2p(imf->name, tmpstr);
  1432.         DrawString(tmpstr);
  1433.     }
  1434. }
  1435.  
  1436. /* Adjust the scrollbar to reflect the size at which the images are being rendered. */
  1437.  
  1438. void
  1439. set_scrollbar()
  1440. {
  1441.     SetCtlMax(vscrollbar, rows - numvisrows);
  1442.     SetCtlValue(vscrollbar, firstvisrow);
  1443.     HiliteControl(vscrollbar, (numvisrows < rows ? 0 : 255));
  1444. }
  1445.  
  1446. /* Indicate which image is currently selected. */
  1447.  
  1448. void
  1449. invert_selected_imf()
  1450. {
  1451.     int row, col;
  1452.     Rect tmprect;
  1453.  
  1454.     if (selected_imf != NULL) {
  1455.         col = selected_n % cols;
  1456.         row = selected_n / cols;
  1457.         /* Calculate the bounding box for the selected image. */
  1458.         tmprect.left = col * eltw;  tmprect.top = toplineh + (row - firstvisrow) * elth;
  1459.         tmprect.right = tmprect.left + eltw;  tmprect.bottom = tmprect.top + elth;
  1460.         if (tmprect.top < toplineh) return;
  1461.         /* This inverts a rectangle around the selected image. */
  1462.         InvertRect(&tmprect);
  1463.         InsetRect(&tmprect, 1, 1);
  1464.         InvertRect(&tmprect);
  1465.     }
  1466. }
  1467.  
  1468. void
  1469. draw_as_terrain_image(int sx, int sy, int sw, int sh, ImageFamily *imf)
  1470. {
  1471.     Rect rect;
  1472.     Image *timg;
  1473.     MacImage *macimg;
  1474.  
  1475.     timg = best_image(imf, sw, sh);
  1476.     if (timg) {
  1477.         rect.left = sx;  rect.top = sy;
  1478.         rect.right = sx + sw;  rect.bottom = sy + sh;
  1479.         macimg = (MacImage *) timg->hook;
  1480.         if (macimg == NULL) {
  1481.             /* a serious error? */
  1482.         } else if (hasColorQD && show_color && macimg->colrpat != nil) {
  1483.             FillCRect(&rect, macimg->colrpat);
  1484.         } else if (macimg->patdefined) {
  1485.             FillRect(&rect, (unsigned char *) &(macimg->monopat));
  1486.         } else {
  1487.             /* If no imagery, just leave blank, don't try to make a default image. */
  1488.         }
  1489.     }
  1490. }
  1491.  
  1492. /* This is similar (but not identical! beware!) to Xconq's main unit drawing routine,
  1493.    but it uses an arbitrary image family instead. */
  1494.  
  1495. void
  1496. draw_as_unit_image(WindowPtr win, int sx, int sy, int sw, int sh, ImageFamily *imf)
  1497. {
  1498.     Rect srcrect, imagerect;
  1499.     BitMap bm, *winbits;
  1500.     Image *uimg;
  1501.     MacImage *macimg;
  1502.  
  1503.     uimg = best_image(imf, sw, sh);
  1504.     if (uimg) {
  1505.         imagerect = win->portRect;
  1506.         imagerect.top += sy;  imagerect.left += sx;
  1507.         imagerect.bottom = imagerect.top + sh;  imagerect.right = imagerect.left + sw;
  1508.         winbits = &(((GrafPtr) win)->portBits);
  1509.         macimg = (MacImage *) uimg->hook;
  1510.         if (macimg == NULL) {
  1511.             /* a serious error? */
  1512.         } else if (macimg->monopict != nil) {
  1513.             DrawPicture(macimg->monopict, &imagerect);
  1514.         } else if (hasColorQD && show_color && macimg->colricon != nil) {
  1515.             PlotCIcon(&imagerect, (CIconHandle) macimg->colricon);
  1516.         } else if (macimg->monoicon != nil) {
  1517.             SetRect(&srcrect, 0, 0, 32, 32);
  1518.             bm.rowBytes = 4;
  1519.             bm.bounds = srcrect;
  1520.             if (macimg->maskicon != nil && show_mask) {
  1521.                 bm.baseAddr = *(macimg->maskicon);
  1522.                 CopyBits(&bm, winbits, &srcrect, &imagerect, srcBic, nil);
  1523.             } else {
  1524.                 /* Draw unit bbox as default mask. (maybe shrink a little??) */
  1525.                 FillRect(&imagerect, QD(white));
  1526.             }
  1527.             bm.baseAddr = *(macimg->monoicon);
  1528.             CopyBits(&bm, winbits, &srcrect, &imagerect, srcOr, nil);
  1529.         } else if (macimg->monosicn != nil) {
  1530.             SetRect(&srcrect, 0, 0, 16, 16);
  1531.             bm.rowBytes = 2;
  1532.             bm.bounds = srcrect;
  1533.             if (macimg->masksicn != nil && show_mask) {
  1534.                 bm.baseAddr = *(macimg->masksicn);
  1535.                 CopyBits(&bm, winbits, &srcrect, &imagerect, srcBic, nil);
  1536.             } else {
  1537.                 /* Draw unit bbox as default mask. (maybe shrink a little??) */
  1538.                 FillRect(&imagerect, QD(white));
  1539.             }
  1540.             bm.baseAddr = *(macimg->monosicn);
  1541.             CopyBits(&bm, winbits, &srcrect, &imagerect, srcOr, nil);
  1542.         } else if ((hasColorQD && show_color && macimg->colrpat) || macimg->patdefined) {
  1543.             draw_as_terrain_image(sx, sy, sw, sh, imf);
  1544.         } else {
  1545.             /* should never be possible? */
  1546.         }
  1547.     }
  1548. }
  1549.  
  1550. /* Draw a given side's emblem. Uses the current GrafPort. */
  1551.  
  1552. void
  1553. draw_as_emblem_image(WindowPtr win, int ex, int ey, int ew, int eh, ImageFamily *imf)
  1554. {
  1555.     Rect srcrect, imagerect;
  1556.     BitMap bm, *winbits;
  1557.     Image *eimg;
  1558.     MacImage *macimg;
  1559.  
  1560.     eimg = best_image(imf, ew, eh);
  1561.     /* If an image is present, display it, otherwise just suppress. */
  1562.     if (eimg) {
  1563.         imagerect = win->portRect;
  1564.         imagerect.top += ey;  imagerect.left += ex;
  1565.         imagerect.bottom = imagerect.top + eh;  imagerect.right = imagerect.left + ew;
  1566.         winbits = &(((GrafPtr) win)->portBits);
  1567.         macimg = (MacImage *) eimg->hook;
  1568.         if (macimg == NULL) {
  1569.             /* a serious error? */
  1570.         } else if (macimg->monopict != nil) {
  1571.             DrawPicture(macimg->monopict, &imagerect);
  1572.         } else if (hasColorQD && show_color && macimg->colricon != nil) {
  1573.             PlotCIcon(&imagerect, (CIconHandle) macimg->colricon);
  1574.         } else if (macimg->monoicon != nil) {
  1575.             SetRect(&srcrect, 0, 0, 32, 32);
  1576.             bm.rowBytes = 4;
  1577.             bm.bounds = srcrect;
  1578.             if (macimg->maskicon != nil && show_mask) {
  1579.                 bm.baseAddr = *(macimg->maskicon);
  1580.                 CopyBits(&bm, winbits, &srcrect, &imagerect, srcBic, nil);
  1581.             } else {
  1582.                 /* Draw unit bbox as default mask. (maybe shrink a little??) */
  1583.                 FillRect(&imagerect, QD(white));
  1584.             }
  1585.             bm.baseAddr = *(macimg->monoicon);
  1586.             CopyBits(&bm, winbits, &srcrect, &imagerect, srcOr, nil);
  1587.         } else if (macimg->monosicn != nil) {
  1588.             SetRect(&srcrect, 0, 0, 16, 16);
  1589.             bm.rowBytes = 2;
  1590.             bm.bounds = srcrect;
  1591.             if (macimg->masksicn != nil && show_mask) {
  1592.                 bm.baseAddr = *(macimg->masksicn);
  1593.                 CopyBits(&bm, winbits, &srcrect, &imagerect, srcBic, nil);
  1594.             } else {
  1595.                 /* Draw unit bbox as default mask. (maybe shrink a little??) */
  1596.                 FillRect(&imagerect, QD(white));
  1597.             }
  1598.             bm.baseAddr = *(macimg->monosicn);
  1599.             CopyBits(&bm, winbits, &srcrect, &imagerect, srcOr, nil);
  1600.         } else {
  1601.             /* should never be possible? */
  1602.         }
  1603.     }
  1604. }
  1605.  
  1606. /* Lisp reader support. */
  1607.  
  1608. void
  1609. announce_read_progress()
  1610. {
  1611. }
  1612.  
  1613. char *
  1614. copy_pascal_string(char *str)
  1615. {
  1616.     int len = str[0];
  1617.     char *rslt;
  1618.  
  1619.     rslt = (char *) xmalloc(len + 1);
  1620.     strncpy(rslt, str+1, len);
  1621.     rslt[len] = '\0';
  1622.     return rslt;
  1623. }
  1624.  
  1625. void
  1626. low_init_warning(str)
  1627. char *str;
  1628. {
  1629.     /* Cursor may be weird from loading, reset it. */
  1630.     SetCursor(&QD(arrow));
  1631.     c2p(str, tmpstr);
  1632.     ParamText(tmpstr, "\p", "\p", "\p");
  1633.     switch (CautionAlert(aWarning, nil)) {
  1634.         case 1:
  1635.             /* Just keep going, hope that the warning was a false alarm. */
  1636.             /* (if option key on, should suppress future warnings) */
  1637.             break;
  1638.         default:
  1639.             ExitToShell();
  1640.             break;
  1641.     }
  1642. }
  1643.  
  1644. /* An error is immediately fatal, no recourse. */
  1645.  
  1646. void
  1647. low_init_error(str)
  1648. char *str;
  1649. {
  1650.     /* Cursor may be weird from loading, reset it. */
  1651.     SetCursor(&QD(arrow));
  1652.     c2p(str, tmpstr);
  1653.     ParamText(tmpstr, "\p", "\p", "\p");
  1654.     StopAlert(aError, nil);
  1655.     ExitToShell();
  1656. }
  1657.  
  1658. /* Map these to init_ versions, no "running" in this program. */
  1659.  
  1660. void
  1661. low_run_warning(str)
  1662. char *str;
  1663. {
  1664.     low_init_warning(str);
  1665. }
  1666.  
  1667. void
  1668. low_run_error(str)
  1669. char *str;
  1670. {
  1671.     low_init_error(str);
  1672. }
  1673.  
  1674. int
  1675. keyword_code(char *str)
  1676. {
  1677.     run_warning("fake keyword_code being called");
  1678.     return 0;
  1679. }
  1680.  
  1681. /* Make the table so keyword lookup works. */
  1682.  
  1683. struct a_key {
  1684.     char *name;
  1685. } keywordtable[] = {
  1686.  
  1687. #undef  DEF_KWD
  1688. #define DEF_KWD(NAME,code,value)  { NAME },
  1689.  
  1690. #include "keyword.def"
  1691.  
  1692.     { NULL }
  1693. };
  1694.  
  1695. char *keyword_name(enum keywords k)
  1696. {
  1697.     return keywordtable[k].name;
  1698. }
  1699.  
  1700. int
  1701. keyword_value(enum keywords k)
  1702. {
  1703.     run_warning("fake keyword_value being called");
  1704.     return 0;
  1705. }
  1706.  
  1707. /* Fake definitions of unneeded routines called by lisp.c. */
  1708.  
  1709. void
  1710. init_predefined_symbols()
  1711. {
  1712. }
  1713.  
  1714. int
  1715. lazy_bind(Obj *sym)
  1716. {
  1717.     return 0;
  1718. }
  1719.  
  1720.  
  1721.  
  1722.